Vue.js实战 您所在的位置:网站首页 火箭 发射 动画 Vue.js实战

Vue.js实战

2024-06-30 16:45| 来源: 网络整理| 查看: 265

一、目标

    按照项目需求,需要完成如下几部分的功能:

    1、长按屏幕时,显示能量条动画(类似环形进度条);

    2、当能量条充满时,发射小火箭;

二、实现效果

三、步骤

    按照需求分析,上述目标可以拆解成3个功能:

    1)用Vue实现屏幕长按事件;

    2)实现能量条动画;

    3)实现火箭发射动画;

    1、先说下捕捉屏幕长按事件,在IOS、Android等平台都有API直接可用,但在H5中则需要自己去实现。当然目前也有很多开源的Vue长按组件,但本需求相对简单,没有必要因为一个小功能,引入一个包含了其它复杂功能的组件。长按事件的核心是要添加2个事件监听:touchstart和touchend,本人封装成了一个独立的js,touch.js代码如下(完整功能代码详见git):

export default class Touch { constructor(node, continueTime) { this.node = node; this.continueTime = continueTime * 1000; this.touchPoint = { x: 0, y: 0 }; this.startTime = undefined; this.endTime = undefined; this.timerTag = undefined; this.progress = undefined; } registerTouchStart(param) { let self = this; self.node.addEventListener("touchstart", (e) => { console.log("start touch"); clearInterval(self.timerTag); if (e.preventDefault) { e.preventDefault(); } self.touchPoint = { x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY }; console.log("start touch point:" + JSON.stringify(self.touchPoint)); self.startTime = new Date().getTime(); self.timerTag = setInterval(() => { self.endTime = new Date().getTime(); let continueTime = (self.endTime - self.startTime) % self.continueTime; // console.log("continue time:" + continueTime); self.progress = (continueTime / self.continueTime).toFixed(2); if (self.progress == 0) { self.progress = 1.00; } // console.log("current progress:" + self.progress); param.success(self.progress); }, 500); console.log("current timer:" + self.timerTag); }); } registerTouchEnd(param) { let self = this; self.node.addEventListener("touchend", (e) => { console.log("end touch"); clearInterval(self.timerTag); console.log("clear current timer:" + self.timerTag); if (e.preventDefault) { e.preventDefault(); } self.endTime = new Date().getTime(); let deltaX = e.changedTouches[0].pageX - self.touchPoint.x; let deltaY = e.changedTouches[0].pageY - self.touchPoint.y; console.log("start touch point:" + JSON.stringify(e.changedTouches[0])); if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 100) { console.log("no long touch action."); return; } console.log("final progress:" + self.progress); param.success(self.progress == 1); }); } }

    2、在vue页面加载完时,就要注册长按监听事件,即在vue的mounted挂载方法中调用上述js,代码详见第3步:

    3、能量条采用了svg绘制,Rocket.vue页面代码如下:

import Touch from "../commons/touch"; export default { name: "Rocket", data() { return { percent: 0, radius: 45, showRocket: false }; }, mounted: function() { let self = this; let node = self.$refs["rocket-circle"]; let touch = new Touch(node, 5); touch.registerTouchStart({ success: percent => { console.log("current percent:" + percent); self.percentChange(percent); } }); touch.registerTouchEnd({ success: result => { console.log("current result:" + result); if (result) { self.shotRocket(); } } }); }, methods: { shotRocket: function() { let self = this; let rocket = self.$refs["rocket"]; let maxHight = window.screen.availHeight; let timer = setInterval(() => { let bottom = parseInt(rocket.style.bottom); if (!bottom) { bottom = 15; } bottom += 18; console.log("start move to:" + bottom); rocket.style.bottom = bottom + "px"; if (bottom >= maxHight) { clearInterval(timer); console.log("finish move:" + bottom); self.showRocket = false; } }, 100); }, percentChange: function(percent) { let self = this; let input = this.$refs["percent"]; let cirle = self.$refs["circle-bar"]; let rocket = self.$refs["rocket"]; if (isNaN(percent)) { percent = 0; } else { let r = self.radius; let c = Math.PI * (r * 2); if (percent < 0) { percent = 0; } if (percent > 1) { percent = 1; } if (percent > 0 && !self.showRocket) { self.showRocket = true; rocket.style.bottom = "15px"; } let pct = percent * c; cirle.style.strokeDashoffset = c - pct; cirle.style.strokeDashArray = pct + " " + (c - pct); cirle.style.stroke = "#ff9f1e"; self.percent = percent * 100; } } } }; #rocket { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; } .rocket { height: 100px; width: 100px; position: absolute; z-index: 1000; bottom: 15px; left: 50%; margin-left: -50px; display: flex; justify-content: center; align-items: center; } .rocket img { height: 60px; width: 60px; } svg { transform: rotate(-90deg); } svg circle { stroke-dashoffset: 0px; stroke: #666; stroke-width: 10px; } svg .circle-inner { stroke-dasharray: 282.74px; stroke: #666; } svg .circle-bar { stroke-dasharray: 282.74px; transition: stroke-dasharray 0.5s linear; } .rocket-circle { height: 100px; width: 100px; box-shadow: 0 0 5px black; border-radius: 100%; position: absolute; z-index: 1000; bottom: 15px; left: 50%; margin-left: -50px; } .rocket-circle:after { height: 80px; width: 80px; box-shadow: inset 0 0 5px black; content: attr(data-pct) "%"; border-radius: 100%; line-height: 80px; font-size: 10px; text-shadow: 0 0 5px black; margin-top: -40px; margin-left: -40px; position: absolute; z-index: 1000; bottom: 15px; left: 50%; top: 50%; display: flex; justify-content: center; } .circle-input { width: 100px; position: absolute; z-index: 1000; bottom: 155px; left: 50%; margin-left: -50px; }

    4、根据上述的touch.js中设置的长按时长,换算成能量环的充值百分比返回给能量环,能量环通过改变svg circle的stroke-dasharray和stroke-offset来实现充值过程的动画(代码详见第3步)。

    注意:

    1)svg的transform:rotate用来控制circle的起始原点,起始原点默认在水平X轴的圆环上,现在反向旋转90度(逆时针),即圆的起始原点变成了Y轴;

   2)stroke-dasharray表示虚线,通过一组数字来表示实现、虚线(空格隔开,先实后虚,且该数组为偶数,如果为奇数自动X2),比如上述代码中就同时设置了实线和虚线;stroke-offset来表示虚线的起始位置;

    5、火箭发射动画则比较简单,在能量环充满且长按结束时,就设置一个定时器,不停的修改小火箭的bottom值,当bottom值超过屏幕时,停止定时器;

四、总结

    1、vue长按事件相对较容易,在网上找到相关的原理就很好实现;

    2、svg绘图理解起来比较痛苦,尤其是刚开始的时候,动画是根据stroke-offset来的,导致圆环绘制不规则,后面改成了按照stroke-dasharray来,且同时设置了stroke-dasharray的实线和虚线部分,动画才规律;后面把圆环动画的起始位置从水平轴改到垂直轴又花了不少时间,主要还是不了解;

   3、火箭发射比较简单,但是组合完成这2个动画,足足耗费了我3天时间,希望读者可以少走弯路;

五、参考资料

[1]https://www.jb51.net/article/152307.htm

[2]https://www.cnblogs.com/padding1015/p/9277922.html

 

上一篇:Vue.js实战——单独封装echarts时间轴高级篇_16



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有